/**
 * The Woosta Table Football Engine
 *
 * Released under the terms of the Worcester Public License,
 * see LICENSE for details.
 **
 * Engine Kernel - Buffers
 *
 * Provides template classes for memory managed buffers of
 * any type.
 **
 * Author: Sean Micklethwaite
 **/


#ifndef __bufH__
#define __bufH__

#include "mm.h"


namespace WtfEngine
{
	template <class T, unsigned Size>
	class StaticBuffer : public GcObject
	{
	private:
		T	Data[Size];

	public:
		StaticBuffer() {
			memset(Data, 0, sizeof(T) * Size);
		};

		inline T* getData() {
			return Data;
		};
		inline const T* getData() const {
			return Data;
		};

		inline T& operator [] (unsigned i) {
			assert(i < Size);
			return Data[i];
		};
		inline const T& operator [] (unsigned i) const {
			assert(i < Size);
			return Data[i];
		};

		GC_AUTOSIZE(StaticBuffer);
	};

	template <class T = unsigned char>
	class Buffer: public GcObject {
		private:
		T *			Data;
		unsigned	Count;

	public:
		typedef T tElem;

		/// New buffer of size c allocated
		explicit Buffer(unsigned c)
			: Data(new T[c]), Count(c) {
		};
		/// Buffer takes ownership of the passed data
		/// @warning Use the const version to copy the buffer.
		Buffer(T * pData, unsigned c)
			: Data(pData), Count(c) {
		};
		/// Here we must copy, as the passed buffer cannot be
		/// modified.
		Buffer(const T * pData, unsigned c)
		: Data(new T[c]), Count(c) {
			for(int i = 0; i < c; i++) {
				Data[i] = pData[i];
			};
		};

		/// Copy constructor
		Buffer(const Buffer<T>& b)
		: Data(new T[b.Count]), Count(b.Count)
		{
			for(int i = 0; i < Count; i++) {
				Data[i] = b.Data[i];
			};
		};

		~Buffer() {
			delete [] Data;
		};

		inline T* getData() {
			return Data;
		};
		inline const T* getData() const {
			return Data;
		};
		inline unsigned getCount() const {
			return Count;
		};

		inline T& operator [] (unsigned i) {
			assert(i < Count);
			return Data[i];
		};
		inline const T& operator [] (unsigned i) const {
			assert(i < Count);
			return Data[i];
		};

		GC_OBJECT(Buffer, sizeof(Buffer) + (sizeof(T) * Count));
	};

	/**
	 * This class allows arbitrary blocks of memory to be passed around
	 * and shared safely, without the need for copying.
	 **/
	template <class T = unsigned char>
	class NonCopyBuffer : public GcObject
	{
	public:
		typedef Buffer<> tBuffer;
		static const int BlocksPerElem = sizeof(T) / sizeof(tBuffer::tElem);

	private:
		tBuffer::tRef mrStorage;

	public:
		/// New buffer of size c allocated
		explicit NonCopyBuffer(unsigned c)
			: mrStorage(new tBuffer(c * BlocksPerElem)) {
		};
		NonCopyBuffer(tBuffer::tRefa rBuf): mrStorage(rBuf)
		{};

		/// Buffer takes ownership of the passed data
		/// @warning Use the const version to copy the buffer.
		NonCopyBuffer(T * pData, unsigned c)
			: mrStorage(new tBuffer((tBuffer::tElem *)pData, c * BlocksPerElem)) {
		};
		/// Here we must copy, as the passed buffer cannot be
		/// modified.
		NonCopyBuffer(const T * pData, unsigned c)
			: mrStorage(new tBuffer((const tBuffer::tElem *)pData, c * BlocksPerElem))
		{ };

		/// Copy constructor
		NonCopyBuffer(const NonCopyBuffer<T>& b)
			: mrStorage(b.mrStorage)
		{ };

		/// Explicit cast from other buffer types
		template <class U>
		explicit NonCopyBuffer(const NonCopyBuffer<U>& b)
			: mrStorage(b.mrStorage)
		{ };

		inline T* getData() {
			return (T *)mrStorage->getData();
		};
		inline const T* getData() const {
			return (const T *)mrStorage->getData();
		};
		inline unsigned getCount() const {
			return mrStorage->getCount() / BlocksPerElem;
		};

		inline T& operator [] (unsigned i) {
			assert(i < getCount());
			return getData()[i];
		};
		inline const T& operator [] (unsigned i) const {
			assert(i < getCount());
			return getData()[i];
		};

		GC_AUTOSIZE(NonCopyBuffer);
	};
};

#endif
